﻿#region Copyright
// Copyright (c) 2011-2012 RaisingForce Team (Alikin Sergey)
//
// Permission is hereby granted, free of charge, to any person
// obtaining a copy of this software and associated documentation
// files (the "Software"), to deal in the Software without
// restriction, including without limitation the rights to use,
// copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following
// conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
// OTHER DEALINGS IN THE SOFTWARE.
#endregion

using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics.Contracts;
using System;

namespace RaisingForce.Raise.Language.RStates
{
    sealed public class CounterValue :
        RState,
        IEquatable<CounterValue>
    {
        #region Constructors

        private CounterValue()
        {
            this.value = CounterValue.NoneValue;
            this.hasValue = false;
        }

        private CounterValue(int value)
        {
            Contract.Requires(CounterValue.MinValue <= value && value <= CounterValue.MaxValue);
            this.value = value;
            this.hasValue = true;
        }

        #endregion

        #region Private Declarations

        private readonly int value;

        private readonly bool hasValue;

        [ContractInvariantMethod]
        private void ObjectInvariant()
        {
            Contract.Invariant(
                this.value == CounterValue.NoneValue
                || CounterValue.MinValue <= this.value && this.value <= CounterValue.MaxValue);
        }

        #endregion

        #region Private Static Initialization

        private static readonly CounterValue noneState =
            new CounterValue();

        private static readonly ReadOnlyCollection<CounterValue> valueStates;

        static CounterValue()
        {
            int count = CounterValue.MaxValue - CounterValue.MinValue + 1;
            List<CounterValue> valueStates = new List<CounterValue>(count);
            for (int valueIndex = 0; valueIndex < count; valueIndex++)
            {
                int value = valueIndex + CounterValue.MinValue;
                valueStates.Add(
                    new CounterValue(value));
            }
            CounterValue.valueStates = valueStates.AsReadOnly();
        }

        #endregion

        #region Constants

        public const int NoneValue = 0;

        public const int MinValue = 1;

        public const int MaxValue = 9;

        #endregion

        #region Properties

        public int Value
        {
            get
            {
                Contract.Ensures(
                    Contract.Result<int>() == CounterValue.NoneValue
                    || CounterValue.MinValue <= Contract.Result<int>() && Contract.Result<int>() <= CounterValue.MaxValue);
                return this.value;
            }
        }

        public bool HasValue
        {
            get
            {
                return this.hasValue;
            }
        }

        #endregion

        #region Public Methods

        public CounterValue GetNext()
        {
            if (this.value == CounterValue.NoneValue
                || this.value == CounterValue.MaxValue)
            {
                return null;
            }
            return CounterValue.Get(this.value + 1);
        }

        public CounterValue GetPrevious()
        {
            if (this.value == CounterValue.NoneValue
                || this.value == CounterValue.MinValue)
            {
                return null;
            }
            return CounterValue.Get(this.value - 1);
        }

        #endregion

        #region Base Overrides

        public override bool Equals(object obj)
        {
            return obj is CounterValue ?
                this.Equals((CounterValue)obj) :
                false;
        }

        public override int GetHashCode()
        {
            return this.value.GetHashCode()
                ^ this.hasValue.GetHashCode();
        }

        protected override bool isDefault
        {
            get
            {
                return object.ReferenceEquals(this, CounterValue.Default);
            }
        }

        protected override bool equals(RState other)
        {
            return other is CounterValue ?
                this.Equals((CounterValue)other) :
                false;
        }

        #endregion

        #region IEquatable<CounterValue> Members

        [Pure]
        public bool Equals(CounterValue other)
        {
            return other != null ?
                this.value == other.value
                && this.hasValue == other.hasValue :
                false;
        }

        #endregion

        #region Public Static Members

        public static CounterValue None
        {
            get
            {
                Contract.Ensures(Contract.Result<CounterValue>() != null);
                return CounterValue.noneState;
            }
        }

        public static CounterValue Default
        {
            get
            {
                Contract.Ensures(Contract.Result<CounterValue>() != null);
                return CounterValue.noneState;
            }
        }

        public static CounterValue Get(int value)
        {
            if (CounterValue.MinValue > value || value > CounterValue.MaxValue)
            {
                throw new ArgumentOutOfRangeException("value");
            }
            Contract.Ensures(Contract.Result<CounterValue>() != null);
            Contract.EndContractBlock();
            int valueIndex = value - CounterValue.MinValue;
            return CounterValue.valueStates[valueIndex];
        }

        #endregion
    }
}
